﻿/* Copyright (c) Microsoft Corporation
 * 
 * All rights reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.  You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * 
 * THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
 * 
 * See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Threading;
using System.Diagnostics;

namespace ProximityTapper
{
    class ProximityMessage
    {
        public const int MESSAGE_TYPE_BYTES = 502 * 2;
        public const int MESSAGE_DATA_BYTES = 10240;
        public const int MESSAGE_PACKET_SIZE = MESSAGE_TYPE_BYTES + 4 + MESSAGE_DATA_BYTES;

        public ProximityMessage(byte[] packet, ProximityDeviceConnectInfo sourceDeviceInfo, CrossoverConnection connection)
        {
            if (packet.Length != MESSAGE_PACKET_SIZE)
            {
                throw new Exception("Invalid packet size for proximity message");
            }

            SourceDevice = sourceDeviceInfo;
            MessageType = System.Text.Encoding.Unicode.GetString(packet, 0, MESSAGE_TYPE_BYTES).TrimEnd(new char[] {'\0'});
            uint len = BitConverter.ToUInt32(packet, MESSAGE_TYPE_BYTES);
            Data = new byte[len];
            Array.Copy(packet, MESSAGE_TYPE_BYTES + 4, Data, 0, len);

            Connection = connection;

            // Try parsing message
            MessageData = ProximityTapper.MessageParser.TryParse(Data, MessageType);
        }

        public ProximityMessage(string type, string data, bool nullTerminate, CrossoverConnection connection)
        {
            if ((type.Length * 2) > MESSAGE_TYPE_BYTES || ((data.Length + (nullTerminate ? 1 : 0)) * 2) > MESSAGE_DATA_BYTES)
            {
                throw new Exception("Type or data for message exceeds size limitation");
            }

            MessageType = type;

            Data = new byte[(data.Length + (nullTerminate ? 1 : 0)) * 2];
            System.Text.Encoding.Unicode.GetBytes(data, 0, data.Length, Data, 0);
            Connection = connection;

            // Create new MessageData holding String
            MessageData = new MessageStringData(data);
        }

        public ProximityMessage(string type, byte[] data, CrossoverConnection connection)
        {
            if ((type.Length * 2) > MESSAGE_TYPE_BYTES || data.Length > MESSAGE_DATA_BYTES)
            {
                throw new Exception("Type or data for message exceeds size limitation");
            }

            MessageType = type;
            Data = data;

            Connection = connection;

            // Create new MessageData holding String
            MessageData = new MessageByteData(data);
        }

        public byte[] ToPacketBytes()
        {
            var packet = new byte[MESSAGE_PACKET_SIZE];

            System.Text.Encoding.Unicode.GetBytes(MessageType, 0, MessageType.Length, packet, 0);

            var len = BitConverter.GetBytes((uint)(Data.Length));
            len.CopyTo(packet, MESSAGE_TYPE_BYTES);
            Data.CopyTo(packet, MESSAGE_TYPE_BYTES + 4);

            return packet;
        }

        public byte[] Data;

        private ProximityDeviceConnectInfo _sourceDevice;
        public ProximityDeviceConnectInfo SourceDevice
        {
            get { return _sourceDevice; }
            set { if (_sourceDevice != value) { _sourceDevice = value; OnPropertyChanged("SourceDevice"); } }
        }

        private List<ProximityDeviceConnectInfo> _targetDevices = new List<ProximityDeviceConnectInfo>();
        public List<ProximityDeviceConnectInfo> TargetDevices
        {
            get { return _targetDevices; }
            set { if (_targetDevices != value) { _targetDevices = value; OnPropertyChanged("TargetDevices"); OnPropertyChanged("TargetDevicesCaption"); } }
        }

        public string TargetDevicesCaption
        {
            get
            {
                string result = "";
                foreach (var target in TargetDevices)
                {
                    result += target.Caption + "\n";
                }
                return result.TrimEnd(new char[] {'\n'});
            }
        }

        public ProximityTapper.IMessageData MessageData { get; private set; }

        private DateTime _receivedDate = DateTime.Now;
        public DateTime ReceivedDate
        {
            get { return _receivedDate; }
            set { if (_receivedDate != value) { _receivedDate = value; OnPropertyChanged("ReceivedDate"); } }
        }

        private string _messageType;
        public string MessageType
        {
            get { return _messageType; }
            set { if (_messageType != value) { _messageType = value; OnPropertyChanged("MessageType"); } }
        }
        
        public string DataAsAutoString
        {
            get
            {
                return this.MessageData.ToString();
            }
        }

        public CrossoverConnection Connection
        {
            get;
            set;
        }

        public override string ToString()
        {
            return MessageType + ": " + DataAsAutoString;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                System.Windows.Application.Current.Dispatcher.BeginInvoke(new ThreadStart(() =>
                {
                    handler(this, new PropertyChangedEventArgs(name));
                }));
            }
        }
    }
}
